/*
MIT License 

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов, 

https://bmstu.codes/lsx/simodo
*/

#ifndef simodo_core_PushdownAutomaton
#define simodo_core_PushdownAutomaton

/*! \file PushdownAutomaton.h
    \brief Выталкивающий конечный автомат (Pushdown automaton)
*/

#include "simodo/parser/Grammar.h"
#include "simodo/parser/automaton/AstBuilder_interface.h"
#include "simodo/inout/reporter/Reporter_abstract.h"
#include "simodo/inout/token/Parser_interface.h"
#include "simodo/inout/token/Token.h"
#include "simodo/inout/token/Tokenizer.h"

#include <string>


namespace simodo::parser
{
    /*!
     * \brief Состояние разбора
     *
     * Объекты класса используются в качестве элементов стека состояния конечного автомата разбора.
     *
     * Неизменяемый (immutable) класс.
     */
    class PushdownAutomatonState: public inout::Token
    {
        // inout::Token token;  ///< 
        size_t state_no;    ///< Номер состояния парсера (строка в таблице разбора)
        size_t symbol_no;   ///< Номер символа грамматики (колонка в таблице разбора)

    public:
        /*!
         * \brief Конструктор состояния разбора
         * \param state_no  Номер состояния парсера (строка в таблице разбора)
         * \param symbol_no Номер символа грамматики (колонка в таблице разбора)
         * \param token     Токен
         */
        PushdownAutomatonState(size_t state_no, size_t symbol_no, const inout::Token &token)
            : inout::Token(token)
            , state_no(state_no)
            , symbol_no(symbol_no)
        {
        }

        /*!
         * \brief Геттер номера состояния парсера (строка в таблице разбора)
         * \return Номер состояния парсера (строка в таблице разбора)
         */
        size_t getStateNo(void) const { return state_no; }

        /*!
         * \brief Геттер номера символа грамматики (колонка в таблице разбора)
         * \return Номер символа грамматики (колонка в таблице разбора)
         */
        size_t getSymbolNo(void) const { return symbol_no; }
    };

    /*!
     * \brief Выталкивающий конечный автомат (Pushdown automaton)
     *
     * Разновидность детерминированного автомата с магазинной памятью,
     * позволяющая выполнять разбор детерминированных контекстно-свободных языков.
     *
     * Парсер реализует табличный метод разбора снизу-вверх семейства *LR(1).
     * В конструктор должна быть передана заполненная грамматика,
     * в которой хранится таблица переходов, определяющая программу автомата.
     *
     * InputStream_interface определяет поток входных символов,
     * который предварительно обрабатывается токенайзером.
     *
     * AstBuilder_interface определяет "строителя" (шаблон проектирования),
     * формирующего дерево разбора. Это может быть каноническое абстрактное синтаксическое
     * дерево (АСД) или семантическое дерево (как реализовано в SIMODO), т.к. вместе с
     * символами, участвующими в свёртке передаётся вставка кода, заданная в описании
     * грамматики.
     *
     * vector<PushdownAutomatonState> st_stack определеяет стек состояний автомата.
     */
    class PushdownAutomaton : public inout::Parser_interface
    {
        // std::u16string              _file_name;     ///< Наименование файла, который парсим
        inout::Reporter_abstract &  _m;             ///< Обработчик сообщений
        Grammar &                   _g;             ///< Грамматика
        const inout::uri_set_t &    _files;         ///< Перечень файлов
        inout::uri_index_t          _uri_index;     ///< Индекс разбираемого файла
        AstBuilder_interface &      _builder;       ///< Построитель дерева операционной семантики

    public:
        PushdownAutomaton() = delete;  ///< Пустой конструктор не поддерживается!

        /*!
         * \brief Конструктор парсера
         * \param m         Интерфейсная ссылка на объект обеспечения вывода информации в вызываемую программу
         * \param g         Грамматика языка заданного файла
         * \param files     Перечень файлов (разбирается всегда последний)
         */
        PushdownAutomaton(inout::Reporter_abstract & m, 
                          Grammar & g, 
                          const inout::uri_set_t & files);

        /*!
         * \brief Конструктор парсера
         * \param m         Интерфейсная ссылка на объект обеспечения вывода информации в вызываемую программу
         * \param g         Грамматика языка заданного файла
         * \param files     Перечень файлов (разбирается всегда последний)
         * \param builder   Интерфейсная ссылка на объект построения АСД
         */
        PushdownAutomaton(inout::Reporter_abstract & m, 
                          Grammar & g,
                          const inout::uri_set_t & files, 
                          AstBuilder_interface & builder);

        /*!
         * \brief Метод разбора текста из файла, указанного в конструкторе
         *
         * Метод создаёт входной поток из заданного в конструкторе файла и вызывает одноимённый метод.
         *
         * \return true, если разбор закончился без ошибок, иначе - false
         */
        virtual bool parse() override;

        /*!
         * \brief Метод разбора текста из интерфейса входного потока
         *
         * Метод позволяет выполнять разбор текста с любого входного потока,
         * в том числе из буфера в памяти, что важно при работе с интегрированной средой разработки.
         *
         * \param stream    Интерфейс входного потока
         * \return true, если разбор закончился без ошибок, иначе - false
         */
        virtual bool parse(inout::InputStream_interface & stream) override;

        /*!
         * \brief Метод выполняет построение параметров для лексического анализатора
         *
         * Метод выполняет построение параметров для лексического анализатора (токенайзера)
         * по данным из структуры грамматики, заданной в конструкторе.
         *
         * \return Параметры лексического анализатора
         */
        // static inout::LexicalParameters makeLexicalParameters(const Grammar & g);

    protected:
        /*!
         * \brief Прокси-метод для пропуска незначимых токенов, но передачи всех токенов в AstBuilder_interface для сбора статистики
         *
         * \param builder Интерфейс сбора СД и статистики, в котором вызывается метод onTerminal по всем токенам (в т.ч. комментариям)
         * \return inout::Token Значимый токен
         */
        inout::Token   getToken(inout::Tokenizer & tz, AstBuilder_interface & builder) const;

        /*!
         * \brief Метод формирования и отображения отчёта о синтаксической ошибке
         * \param states    Текущее состояние разбора парсера
         * \param t         Текущий токен, приведший к синтаксической ошибке
         */
        void    reportSyntaxError(const std::vector<PushdownAutomatonState> & states, const inout::Token & t) const;

        /*!
         * \brief Метод определяет допустим ли заданный терминал (лексема) для заданного состояния разбора
         * \param states    Состояние разбора
         * \param lexeme    Терминал, который нужно проверить на допустимость для заданного состояния
         * \return true, если терминал допустим, иначе - false
         */
        bool    isLexemeValid(std::vector<PushdownAutomatonState> states, const inout::Lexeme & lexeme) const;
    };


}

#endif // simodo_core_PushdownAutomaton
